package com.test.factory;

import com.test.definition.BeanDefinition;
import com.test.exception.BeansException;
import com.test.processor.BeanPostProcessor;
import com.test.registry.DefaultSingletonRegistry;

import java.util.ArrayList;
import java.util.List;

/**
 * AbstractBeanFactory类是Bean工厂的基础实现类，提供了Bean生命周期管理的模板。
 * 它继承了DefaultSingletonRegistry类，用于管理单例Bean，并实现了BeanFactory接口。
 * 该类提供了一系列方法用于获取Bean实例，包括根据Bean名称，Bean类型，以及根据Bean类型获取Bean。
 * 它通过模板方法模式，将Bean的创建过程委托给子类实现，确保了框架的灵活性和可扩展性。
 */
public abstract class AbstractBeanFactory extends DefaultSingletonRegistry implements BeanFactory {
    // 存储BeanPostProcessor的列表，用于在Bean初始化前后进行处理
    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    /**
     * 添加BeanPostProcessor到列表中
     * 先移除已存在的相同处理器，再添加，以确保处理器列表中该处理器的唯一性和顺序性
     * @param beanPostProcessor 要添加的BeanPostProcessor实例
     */
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        this.beanPostProcessors.remove(beanPostProcessor);
        this.beanPostProcessors.add(beanPostProcessor);
    }

    /**
     * 获取BeanPostProcessor列表
     * @return BeanPostProcessor列表
     */
    public List<BeanPostProcessor> getBeanPostProcessors() {
        return this.beanPostProcessors;
    }

    /**
     * 在Bean初始化后应用一系列的BeanPostProcessor
     * 该方法是抽象的，需要由子类实现具体的逻辑
     * @param existingBean 初始化后的Bean实例
     * @param beanName Bean的名称
     * @return 经过处理后的Bean实例
     * @throws Exception 如果处理过程中发生错误，则抛出异常
     */
    public abstract Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws Exception;

    /**
     * 在Bean初始化前应用一系列的BeanPostProcessor
     * 该方法是抽象的，需要由子类实现具体的逻辑
     * @param existingBean 初始化前的Bean实例
     * @param beanName Bean的名称
     * @return 经过处理后的Bean实例
     * @throws Exception 如果处理过程中发生错误，则抛出异常
     */
    public abstract Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws Exception;

    /**
     * 根据Bean名称获取Bean实例。
     *
     * @param name Bean的名称。
     * @return Bean实例。
     * @throws BeansException 如果获取Bean实例过程中出现错误。
     */
    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null);
    }

    /**
     * 根据Bean名称和所需的Bean类型获取Bean实例。
     *
     * @param name Bean的名称。
     * @param requiredType 所需的Bean类型。
     * @return 指定类型的Bean实例。
     * @throws BeansException 如果获取Bean实例过程中出现错误。
     */
    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return doGetBean(name, requiredType);
    }

    /**
     * 根据所需的Bean类型获取Bean实例，使用类型名称作为Bean名称。
     *
     * @param requiredType 所需的Bean类型。
     * @return 指定类型的Bean实例。
     * @throws BeansException 如果获取Bean实例过程中出现错误。
     */
    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return getBean(requiredType.getName(), requiredType);
    }

    /**
     * 检查是否包含指定名称的Bean。
     *
     * @param name Bean的名称。
     * @return 如果包含指定名称的Bean则返回true，否则返回false。
     */
    @Override
    public boolean containsBean(String name) {
        return containsSingleton(name) || containsBeanDefinition(name);
    }

    /**
     * 实际执行获取Bean实例的操作。
     * 首先尝试从单例池中获取Bean，如果获取不到，则根据Bean名称获取Bean定义，并创建Bean实例。
     *
     * @param name Bean的名称。
     * @param requiredType 所需的Bean类型，可以为null。
     * @return 指定类型的Bean实例。
     * @throws BeansException 如果获取Bean实例过程中出现错误。
     */
    protected <T> T doGetBean(String name, Class<T> requiredType) throws BeansException {
        Object bean = getSingleton(name);
        if (bean != null) {
            return (T) bean;
        }
        BeanDefinition beanDefinition = getBeanDefinition(name);
        return (T) createBean(name, beanDefinition, null);
    }

    /**
     * 检查是否包含指定名称的Bean定义。
     *
     * @param beanName Bean的名称。
     * @return 如果包含指定名称的Bean定义则返回true，否则返回false。
     */
    protected abstract boolean containsBeanDefinition(String beanName);

    /**
     * 根据Bean名称获取Bean定义。
     *
     * @param beanName Bean的名称。
     * @return Bean定义。
     * @throws BeansException 如果获取Bean定义过程中出现错误。
     */
    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    /**
     * 根据Bean名称、Bean定义和构造参数创建Bean实例。
     *
     * @param beanName Bean的名称。
     * @param beanDefinition Bean定义。
     * @param args 构造参数，可以为null。
     * @return 创建的Bean实例。
     * @throws BeansException 如果创建Bean实例过程中出现错误。
     */
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException;
}
